/** file InstrumentMultiphonique.cpp
    author belot.nicolas
*/

#include "InstrumentMultiphonique.h"
#include "SampleFactory.h"
#include "ChangePitch.h"
#include <iostream>
#include <cmath>

namespace libtrckr {

InstrumentMultiphonique::InstrumentMultiphonique():Instrument(){
	_name=string();
	//_tune=string();
	//_partition=queue<INote>();
//	_samples=map<string, INote>();
}

InstrumentMultiphonique::InstrumentMultiphonique(string Name):Instrument(),_name(Name){
	//_tune=string();
	//_partition=queue<INote>();
//	_samples=map<string, INote>();
}

InstrumentMultiphonique::InstrumentMultiphonique(const InstrumentMultiphonique & im):Instrument(im),
	_name(im._name), 
	_tune(im._tune)
	//_partition(im._partition),
{
	//in fact we need to copy samples and attack...
	//will do it later.
}


InstrumentMultiphonique::~InstrumentMultiphonique(){
	for(uint i=0;i< nb_string;i++){
		delete _partitions[i];
		delete current[i];
		for(uint j=0;j<nb_attack;j++){
			delete _samples[j*nb_string+i];
		}
	}

}

string InstrumentMultiphonique::getName() const {
	return _name;
}

DynArray<string> InstrumentMultiphonique::getTuning() const{
	DynArray<string> ret(nb_string);
	for(uint i=0;i< nb_string;i++){
		ret[i] = string(_tune[i]);
	}
	return ret;
}

DynArray<string> InstrumentMultiphonique::getAttacks() const {
	DynArray<string> ret(nb_string);
	for(uint i=0;i<nb_attack;i++){
		ret[i]= string(attack[i]);
	}
	return ret;
}

void InstrumentMultiphonique::setName( string Name){
	_name=Name;
}

void InstrumentMultiphonique::setTuning( DynArray<string> tune){
	_tune=DynArray<string>(tune.size(), string());
	_partitions=DynArray<queue<INote>*>(tune.size(),NULL);
	elapsed=DynArray<uint>(tune.size());
	duration=DynArray<uint>(tune.size());
	mvolume=DynArray<double>(tune.size());
	current =DynArray<Sample*>(tune.size(),NULL);
	effects=DynArray<DynArray<PlayingEffect*>* >(tune.size(),NULL);
	ratio=DynArray<double>(tune.size(),1.);
	for(uint j=0;j<tune.size();j++){
		_tune[j]=string(tune[j]);
		_partitions[j] = new queue<INote>();
	}
	nb_string=tune.size();
}

void InstrumentMultiphonique::setNote( string name, string file,float speed,short panning,short att,short where){// et les cordes?
	INote in;
	in._note = Note(name,Duration::NOIRE,false);
	in.filename=file;
	in.speed=speed;
	if(speed != 1.){
		std::cerr << "Speed set to another value that ZERO. Actually we don't support dynamic modification of sample speed" << std::endl;
	}
	in._panning=panning;
	in.attack=att;
	if(att >= 0 && att < nb_attack && where >=0 && where < nb_string && _samples[att*nb_string+where]!=0)
		_samples[att*nb_string+where]->insert(pair<string, INote>(name, in) );

}

void InstrumentMultiphonique::setAttack(string **att){
	int cnt=0;
	while(att[cnt]!=0){
		cnt++;
	}
	attack=DynArray<string>(cnt,string());
	_samples=DynArray<map<string, INote>*>(cnt*nb_string,NULL);
	for(uint i=0;i<cnt;i++){
		attack[i]=string(*att[i]);
		for(uint j=0;j<nb_string;j++){
			_samples[i*nb_string+j] = new map<string,INote>();
		}
	}
	nb_attack=cnt;
}

void InstrumentMultiphonique::setAttack(DynArray<string> att){
	attack=att;
	_samples=DynArray<map<string,INote>*>(att.size()*nb_string, NULL);
	for(uint i=0;i<att.size();i++){
		attack[i]= att[i];
		for(uint j=0;j<nb_string;j++){
			_samples[i*nb_string+j] = new map<string,INote>();
		}
	}
	nb_attack=att.size();

	
}



Buffer InstrumentMultiphonique::play( uint size){
	Buffer ret = Buffer(size);
	Buffer tmp = Buffer(size);
	for(uint i=0;i<nb_string;i++){
		tmp.flush();
		if(elapsed[i] > size){
			if(current[i]!=0)
				naturalEffects(tmp,*current[i],effects[i]);
				//tmp << *current[i];
			//else we return a blank buffer...
			elapsed[i]-=size;
			if(current[i]->isEOF()){
				current[i]=0;
			}
		}else{
			Buffer tret(elapsed[i]);
			if(current[i]!=0)
				naturalEffects( tret, *current[i],effects[i]);
				//tmp.append(current[i]->read(elapsed[i]));
			if(_partitions[i]->size()>0){
				INote t = _partitions[i]->front();
				_partitions[i]->pop();
				if(t._note.name()=="blank" && !ring){
					current[i]=0;
					tmp.append(tret);
					for(uint j=duration[i]-elapsed[i];j<size;j++){
						tmp[j]=.0;
					}
				}else if(_samples[t.attack*nb_string+i]->find(t._note.name())!=_samples[t.attack*nb_string+i]->end()){
					//we should destroy current[i] ?
					current[i] = SampleFactory::newSample( (*_samples[t.attack*nb_string+i])[t._note.name()].filename.c_str());
					ratio[i]=(*_samples[t.attack*nb_string+i])[t._note.name()].speed;
					mvolume[i]=t._volume;
					if(ratio[i]!=1.0){
						PlayingEffect *pitch = new ChangePitch();
						DynArray<double> conf(1,ratio[i]);
						pitch->Tune(conf);
						int psize = (*_samples[t.attack])[t._note.name()].playing->size()+1;
						effects[i] = new DynArray<PlayingEffect*>(psize , NULL);
						(*effects[i])[0]=pitch;
						for(uint j=0;j<(*_samples[t.attack])[t._note.name()].playing->size();j++){
							(*effects[i])[j+1]=  (*(*_samples[t.attack])[t._note.name()].playing)[j];
						}
					}else{
						effects[i]=(*_samples[t.attack*nb_string+i])[t._note.name()].playing;
					}
					Buffer tret2(size-elapsed[i]);
					naturalEffects(tret2,*current[i],effects[i]);
					tmp+=tret+tret2;
					//tmp << *current[i];
					if(current[i]->isEOF()){
						current[i]=0;
					}
				}else{
					//no such note...should die ?
				}
				int ltmp = size - (duration[i] - elapsed[i]);
				duration[i] = t._note.sampleLength();
				elapsed[i] = duration[i] - ltmp;
			}else{
				elapsed[i]=0;
			}
		}
		for(uint j=0;j<size;j++){
			ret[j]+=(tmp[j]*mvolume[i]);
		}
	}
	return ret;
}
 
Buffer& InstrumentMultiphonique::play( Buffer& b){
	uint size = b.capacity();
	b.flush();
	Buffer tmp = Buffer(size);
	for(uint i=0;i<nb_string;i++){
		tmp.flush();
		if(elapsed[i] > size){
			if(current[i]!=0)
				naturalEffects(b, *current[i],effects[i]);
				//tmp << *current[i];
			//else we return a blank buffer...
			elapsed[i]-=size;
		}else{
			Buffer tret;
			if(current[i]!=0)
				naturalEffects(tret,*current[i],effects[i]);
				//tmp.append(current[i]->read(elapsed[i]));
			if(_partitions[i]->size()>0){
				INote t = _partitions[i]->front();
				_partitions[i]->pop();
				if(t._note.name()=="blank"){
					current[i]=0;
					tmp.append(tret);
					for(uint j=duration[i]-elapsed[i];j<size;j++){
						tmp[j]=.0;
					}
				}else if(_samples[t.attack*nb_string+i]->find(t._note.name())!=_samples[t.attack*nb_string+i]->end()){
					//we should destroy current[i] ?
					current[i] = SampleFactory::newSample( (*_samples[t.attack*nb_string+i])[t._note.name()].filename.c_str());
					ratio[i]=(*_samples[t.attack*nb_string+i])[t._note.name()].speed;
					mvolume[i]=t._volume;
					if(ratio[i]!=1.0){
						PlayingEffect *pitch = new ChangePitch();
						DynArray<double> conf(1,ratio[i]);
						pitch->Tune(conf);
						int psize = (*_samples[t.attack])[t._note.name()].playing->size()+1;
						effects[i] = new DynArray<PlayingEffect*>(psize , NULL);
						(*effects[i])[0]=pitch;
						for(uint j=0;j<(*_samples[t.attack])[t._note.name()].playing->size();j++){
							(*effects[i])[j+1]=  (*(*_samples[t.attack])[t._note.name()].playing)[j];
						}
					}else{
						effects[i]=(*_samples[t.attack*nb_string+i])[t._note.name()].playing;
					}
					Buffer tret2(size-elapsed[i]);
					naturalEffects(tret2,*current[i],effects[i]);
					tmp+=tret+tret2;
					//tmp << *current[i];
				}else{
					//no such note...should die ?
				}
				int ltmp = size - (duration[i] - elapsed[i]);
				duration[i] = t._note.sampleLength();
				elapsed[i] = duration[i] - ltmp;
			}else{
				elapsed[i]=0;
			}
		}
		for(uint j=0;j<size;j++){
			b[j]+=(tmp[j]*mvolume[i]);
		}
	}
	return b;
}

void InstrumentMultiphonique::addNote(string name, Duration::note_duration_t duration, bool point , uint volume,short att,short where,DynArray<PlayingEffect*>* eff=0){
	INote in ;
	in._note = Note(name, duration,point);
	in._volume=pow(10.0, (0.0-volume/100.0)/20.0);
	in.attack=att;
	in.playing=eff;
	if(where >= 0 && where < nb_string)
		_partitions[where]->push(in);
}

void InstrumentMultiphonique::addNote(Note n, uint volume,short att,short where,DynArray<PlayingEffect*>* eff=0){
	INote in;
	in._note=n;
	in._volume=pow(10.0, (0.0-volume/100.0)/20.0);
	in.attack=att;
	in.playing=eff;
	if(where>=0 && where < nb_string)
		_partitions[where]->push(in);
}


bool InstrumentMultiphonique::end() const{
	bool partend=true;
	for(uint i=0;i<nb_string;i++){
		if(_partitions[i]->size()!=0 && elapsed[i]!=0)
			partend=false;
	}
	return partend;
}
void InstrumentMultiphonique::letRing(bool ringing){
	ring=ringing;
}
}

